#[macro_export]
macro_rules! log {
  ($($arg:tt)*) => {{
    log::log(format!($($arg)*));
  }};
}

#[macro_export]
macro_rules! err {
  ($($arg:tt)*) => {{
    log::err(format!($($arg)*));
  }};
}


#[macro_export]
macro_rules! hook {
  () => {};

  (fn $name:ident($($param:ident : $argument_type:ty),*) -> $return_type:ty;) => {
    with_builtin_macros::with_eager_expansions! {
      unsafe extern "C" {
        #[link_name = stringify!($name)]
        pub static #{ concat_idents!($name, _ptr) }: *const c_void;
      }

      static_detour! {
        static #{ concat_idents!(hook_, $name) }: fn($($argument_type),*) -> $return_type;
      }

      fn #{ concat_idents!(attach_, $name) }() -> Result<()> {
        unsafe {
          let target = std::mem::transmute(concat_idents!($name, _ptr) as *const c_void);
          concat_idents!(hook_, $name).initialize(target, $name)?;
        }

        Ok(())
      }

      fn #{ concat_idents!(enable_, $name) }() -> Result<()> {
        unsafe { concat_idents!(hook_, $name).enable()?; }

        Ok(())
      }

      #[allow(dead_code)]
      fn #{ concat_idents!(disable_, $name) }() -> Result<()> {
        unsafe { concat_idents!(hook_, $name).disable()?; }

        Ok(())
      }

      #[allow(dead_code)]
      fn #{ concat_idents!(call_, $name) }($($param : $argument_type),*) -> $return_type {
        concat_idents!(hook_, $name).call($($param),*)
      }
    }
  };

  (fn $name:ident($($param:ident : $argument_type:ty),*);) => {
    hook!{ fn $name($($param : $argument_type),*) -> (); }
  };

  (fn $name:ident($($param:ident : $argument_type:ty),*);
   $($rest:tt)*) => {
    hook!{ fn $name($($param : $argument_type),*) -> (); }
    hook!{ $($rest)* }
  };

  (fn $name:ident($($param:ident : $argument_type:ty),*) -> $return_type:ty;
   $($rest:tt)*) => {
    hook!{ fn $name($($param : $argument_type),*) -> $return_type; }
    hook!{ $($rest)* }
  };
}
